/*
 * The MIT License
 *
 * Copyright (c) 2010, Bharathwaaj Srinivasan, bluegenetic@gmail.com, bharathwaaj.s@gmail.com
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

#include "curltweet_api.h"
#include "private.h"


CtError curltweet_init(void)
{
    curl_global_init(CURL_GLOBAL_ALL);
}

CtError curltweet_cleanup(void)
{
    curl_global_cleanup();
}

CtError curltweet_alloc(CurlTweet *pp_s_ctHandle)
{
    CtError e_retval = ERR_NONE;
    CurlTweet p_s_ctHandle = NULL;

    if (pp_s_ctHandle == NULL) {
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_alloc;
    }

    p_s_ctHandle = (CurlTweet)malloc(sizeof(struct CurlTweetTag));
    if (p_s_ctHandle == NULL) {
        e_retval = ERR_MEMORY_ALLOC;
        goto END_curltweet_alloc;
    }

    memset(p_s_ctHandle, 0, sizeof(struct CurlTweetTag));

    p_s_ctHandle->p_s_curlHandle = curl_easy_init();
    if (p_s_ctHandle->p_s_curlHandle == NULL) {
        e_retval = ERR_INTERNAL;
        goto END_curltweet_alloc;
    }

    (*pp_s_ctHandle) = p_s_ctHandle;
END_curltweet_alloc:
    return e_retval;
}

CtError curltweet_free(CurlTweet p_s_ctHandle)
{
    CtError e_retval = ERR_NONE;

    if (p_s_ctHandle == NULL) {
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_free;
    }

    curl_easy_cleanup(p_s_ctHandle->p_s_curlHandle);

    free(p_s_ctHandle);

END_curltweet_free:
    return e_retval;
}

CtError curltweet_freeStatuses( CurlTweet p_s_ctHandle, StatusRoot *p_s_statuses)
{
    CtError e_retval = ERR_NONE;

    if ((p_s_ctHandle == NULL) || (p_s_statuses == NULL)) {
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_freeStatuses;
    }

    if (p_s_ctHandle->p_s_xml != NULL) {
        ezxml_free(p_s_ctHandle->p_s_xml);
    }
    p_s_ctHandle->p_s_xml = NULL;

    if (p_s_statuses->p_s_statusesArr != NULL) {
        free(p_s_statuses->p_s_statusesArr);
    }

    (p_s_statuses->p_s_statusesArr) = NULL;
    p_s_statuses->u32_numOfStatuses = 0;

END_curltweet_freeStatuses:
    return e_retval;
}

/* Following apis require user to be logged in */
CtError curltweet_logIn(CurlTweet p_s_ctHandle, CtString *p_u8_userName, CtString *p_s_password)
{
    CtError e_retval = ERR_NONE;
    if ((p_s_ctHandle == NULL) || (p_u8_userName->u32_stringLen > CURLTWEET_MAX_USERNAME_SIZE)) {
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_logIn;
    }

    if (p_s_ctHandle->eb_loggedIn == CT_TRUE) {
        /* User already logged in another account
         * Should logout or alloc new handle
         */
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_logIn;
    }

    memcpy(p_s_ctHandle->a_u8_username, p_u8_userName->p_u8_string, p_u8_userName->u32_stringLen);
    memcpy(p_s_ctHandle->a_u8_passwd, p_s_password->p_u8_string, p_s_password->u32_stringLen);
    (p_s_ctHandle->eb_loggedIn = CT_TRUE);

END_curltweet_logIn:
    return e_retval;
}

CtError curltweet_logOut(CurlTweet p_s_ctHandle)
{
    CtError e_retval = ERR_NONE;
    if (p_s_ctHandle == NULL) {
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_logOut;
    }

    memset(p_s_ctHandle->a_u8_username, 0, CURLTWEET_MAX_USERNAME_SIZE);
    memset(p_s_ctHandle->a_u8_passwd, 0, CURLTWEET_MAX_PASSWORD_SIZE);
    (p_s_ctHandle->eb_loggedIn = CT_FALSE);

END_curltweet_logOut:
    return e_retval;
}


/* Authentication required */
CtError curltweet_getTimeline(CurlTweet p_s_ctHandle,
                              Timeline e_timeline,
                              TimelineParams *p_s_optional,
                              StatusRoot *p_s_statuses)
{
    CtError e_retval = ERR_NONE;
    uint8_t a_u8_url[CURLTWEET_MAX_URL_LENGTH];

    if ((p_s_ctHandle == NULL) || (p_s_statuses == NULL)) {
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_getHomeTimeline;
    }

    if ((p_s_ctHandle->eb_loggedIn == CT_FALSE) || (p_s_ctHandle->p_s_xml != NULL)) {
        /* User not logged in or previous message not freed */
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_getHomeTimeline;
    }

    switch(e_timeline) {
    case TIMELINE_PUBLIC:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s", CURLTWEET_PUBLIC_TIMELINE_URL);
        break;
    case TIMELINE_HOME:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s?", CURLTWEET_HOME_TIMELINE_URL);
        break;
    case TIMELINE_FRIENDS:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s?", CURLTWEET_FRIENDS_TIMELINE_URL);
        break;
    case TIMELINE_USER:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s?", CURLTWEET_USER_TIMELINE_URL);
        break;
    case TIMELINE_MENTIONS:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s?", CURLTWEET_MENTIONS_TIMELINE_URL);
        break;
    case TIMELINE_RETWEETED_BY_ME:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s?", CURLTWEET_RETWEETED_BY_ME_TIMELINE_URL);
        break;
    case TIMELINE_RETWEETED_TO_ME:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s?", CURLTWEET_RETWEETED_TO_ME_TIMELINE_URL);
        break;
    case TIMELINE_RETWEETS_OF_ME:
        memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
        sprintf(a_u8_url, "%s?", CURLTWEET_RETWEETS_OF_ME_TIMELINE_URL);
        break;
    }

    if (p_s_optional != NULL) {
        if (p_s_optional->s32_count > 0) {
            sprintf((a_u8_url + strlen(a_u8_url)), "count=%d&", p_s_optional->s32_count);
        }
        if (p_s_optional->s32_page > 0) {
            sprintf((a_u8_url + strlen(a_u8_url)), "count=%d&", p_s_optional->s32_page);
        }
        if (p_s_optional->u64_sinceId > 0) {
            sprintf((a_u8_url + strlen(a_u8_url)), "count=%lld&", p_s_optional->u64_sinceId);
        }
        if (p_s_optional->u64_maxId > 0) {
            sprintf((a_u8_url + strlen(a_u8_url)), "count=%lld&", p_s_optional->u64_maxId);
        }

        if (e_timeline == TIMELINE_USER) {
            /* TODO */
        }
    }
    e_retval = ct_fetch_and_parse (p_s_ctHandle, p_s_statuses, PARSE_STATUSES, a_u8_url);

END_curltweet_getHomeTimeline:
    return e_retval;
}

/* Status Methods */
CtError curltweet_status(CurlTweet p_s_ctHandle,
                         StatusOpt e_status,
                         StatusParams *p_s_params,
                         StatusRoot *p_s_statuses)
{
    CtError e_retval = ERR_NONE;
    uint8_t a_u8_url[CURLTWEET_MAX_URL_LENGTH];
    uint8_t a_u8_tweet[CURLTWEET_MAX_MESSAGE_SIZE + 8] = {"status="};

    if ((p_s_ctHandle == NULL) || (p_s_statuses == NULL)) {
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_status;
    }

    if ((p_s_ctHandle->eb_loggedIn == CT_FALSE) || (p_s_ctHandle->p_s_xml != NULL) || (p_s_params == NULL)) {
        /* User not logged in or previous message not freed */
        e_retval = ERR_INVALID_PARAM;
        goto END_curltweet_status;
    }

    memset(a_u8_url, 0, CURLTWEET_MAX_URL_LENGTH);
    switch(e_status) {
    case STATUS_SHOW:
        sprintf(a_u8_url, "%s%lld.xml", CURLTWEET_STATUS_SHOW_URL, p_s_params->u64_statusId);
        p_s_statuses->p_s_statusesArr = (Status *)malloc(sizeof(Status ));
        p_s_statuses->u32_numOfStatuses = 1;
        e_retval = ct_fetch_and_parse (p_s_ctHandle, p_s_statuses->p_s_statusesArr, PARSE_STATUS, a_u8_url);
        if (e_retval != ERR_NONE) {
            free(p_s_statuses->p_s_statusesArr);
            (p_s_statuses->p_s_statusesArr) = NULL;
            p_s_statuses->u32_numOfStatuses = 0;
        }
        break;

    case STATUS_UPDATE:
        sprintf(a_u8_url, "%s?lat=%f&long=%f", CURLTWEET_STATUS_UPDATE_URL, p_s_params->f32_latitude, p_s_params->f32_longitude);
        p_s_statuses->p_s_statusesArr = (Status *)malloc(sizeof(Status ));
        p_s_statuses->u32_numOfStatuses = 1;
        if (p_s_params->u64_statusId) {
            sprintf((a_u8_url + strlen(a_u8_url)), "&in_reply_to_status_id=%lld", p_s_params->u64_statusId);
        }
        strcat(a_u8_tweet, p_s_params->a_u8_tweet);

        e_retval = ct_post_and_parse (p_s_ctHandle, p_s_statuses->p_s_statusesArr, a_u8_tweet,
                PARSE_STATUS, a_u8_url);
        if (e_retval != ERR_NONE) {
            free(p_s_statuses->p_s_statusesArr);
            (p_s_statuses->p_s_statusesArr) = NULL;
            p_s_statuses->u32_numOfStatuses = 0;
        }
        break;

    case STATUS_DESTROY:
        sprintf(a_u8_url, "%s%lld.xml", CURLTWEET_STATUS_DESTROY_URL, p_s_params->u64_statusId);
        p_s_statuses->p_s_statusesArr = (Status *)malloc(sizeof(Status ));
        p_s_statuses->u32_numOfStatuses = 1;

        e_retval = ct_post_and_parse (p_s_ctHandle, p_s_statuses->p_s_statusesArr, "DELETE",
                PARSE_STATUS, a_u8_url);
        if (e_retval != ERR_NONE) {
            free(p_s_statuses->p_s_statusesArr);
            (p_s_statuses->p_s_statusesArr) = NULL;
            p_s_statuses->u32_numOfStatuses = 0;
        }
        break;

    case STATUS_RETWEET:
        sprintf(a_u8_url, "%s%lld.xml", CURLTWEET_STATUS_RETWEET_URL, p_s_params->u64_statusId);
        p_s_statuses->p_s_statusesArr = (Status *)malloc(sizeof(Status ));
        p_s_statuses->u32_numOfStatuses = 1;

        e_retval = ct_post_and_parse (p_s_ctHandle, p_s_statuses->p_s_statusesArr, "POST",
                PARSE_STATUS, a_u8_url);
        if (e_retval != ERR_NONE) {
            free(p_s_statuses->p_s_statusesArr);
            (p_s_statuses->p_s_statusesArr) = NULL;
            p_s_statuses->u32_numOfStatuses = 0;
        }
        break;
    case STATUS_RETWEETS:
        sprintf(a_u8_url, "%s%lld.xml", CURLTWEET_STATUS_RETWEETS_URL, p_s_params->u64_statusId);

        if (p_s_params->u32_count) {
            sprintf((a_u8_url + strlen(a_u8_url)), "?count=%d", p_s_params->u32_count);
        }
        e_retval = ct_fetch_and_parse (p_s_ctHandle, p_s_statuses, PARSE_STATUSES, a_u8_url);
        break;
    }

END_curltweet_status:
    return e_retval;
}

/* Authentication not required */
CtError curltweet_getUserInfo(CurlTweet p_s_ctHandle,
                              StatusRoot *p_s_statuses,
                              uint64_t *p_u64_userId,
                              CtString *p_s_userName)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_userSearch(CurlTweet p_s_ctHandle,
                             StatusRoot *p_s_statuses,
                             CtString *p_u8_query,
                             uint32_t *p_u32_numOfStatuses,
                             uint32_t *p_u32_pageNo)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required if user is protected, else not required */
/* TODO Update structure for cursor */
CtError curltweet_friendsInfo(CurlTweet p_s_ctHandle,
                              StatusRoot *p_s_statuses,
                              uint64_t *p_u64_userId,
                              CtString *p_s_userName,
                              int64_t *p_s64_cursor)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required if user is protected, else not required */
/* TODO Update structure for cursor */
CtError curltweet_followersInfo(CurlTweet p_s_ctHandle,
                                StatusRoot *p_s_statuses,
                                uint64_t *p_u64_userId,
                                CtString *p_s_userName,
                                int64_t *p_s64_cursor)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* POST LISTS */
/* Authentication required */
CtError curltweet_createNewList(CurlTweet p_s_ctHandle,
                                CtString *p_s_listName,
                                bool_t eb_isPrivate,
                                CtString *p_s_description)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_updateListInfo(CurlTweet p_s_ctHandle,
                                 CtString *p_s_listName,
                                 bool_t eb_isPrivate,
                                 CtString *p_s_description)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
/* TODO List structure to be abstracted
 * FIXME Abstract list structure
 */
CtError curltweet_getLists(CurlTweet p_s_ctHandle,
                           CtString *p_s_userName,
                           int64_t *p_s64_cursor,
                           ListRoot *p_s_lists)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
/* TODO List structure to be abstracted
 * FIXME Abstract list structure
 */
CtError curltweet_showList(CurlTweet p_s_ctHandle,
                           uint64_t u64_listId,
                           List *p_s_listInfo)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_deleteList(CurlTweet p_s_ctHandle,
                             uint64_t u64_listId)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_getListStatuses(CurlTweet p_s_ctHandle,
                                  uint64_t u64_listId,
                                  TimelineParams *p_s_optional,
                                  StatusRoot *p_s_statuses)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_getListMemberShips(CurlTweet p_s_ctHandle,
                                     ListRoot *p_s_lists,
                                     int64_t *p_s64_cursor)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_getListSubscriptions(CurlTweet p_s_ctHandle,
                                       ListRoot *p_s_lists,
                                       int64_t *p_s64_cursor)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_getListMembers(CurlTweet p_s_ctHandle,
                                 uint64_t u64_listId,
                                 int64_t *p_s64_cursor,
                                 StatusRoot *p_s_statuses)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_addMemberToList(CurlTweet p_s_ctHandle,
                                  uint64_t u64_listId,
                                  uint64_t u64_userId)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_deleteMemberFromList(CurlTweet p_s_ctHandle,
                                       uint64_t u64_listId,
                                       uint64_t u64_userId)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_checkMemberFromList(CurlTweet p_s_ctHandle,
                                      uint64_t u64_listId,
                                      uint64_t u64_userId,
                                      bool_t *p_eb_present)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* List Subscribing Options */
/* Authentication required */
CtError curltweet_getListSubscribers(CurlTweet p_s_ctHandle,
                                     uint64_t u64_listId,
                                     int64_t *p_s64_cursor,
                                     StatusRoot *p_s_statuses)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_followList(CurlTweet p_s_ctHandle,
                             uint64_t u64_listId)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
CtError curltweet_unsubscribeFromList(CurlTweet p_s_ctHandle,
                                      uint64_t u64_listId,
                                      uint64_t u64_userId)
{
    CtError e_retval = ERR_NONE;
    printf("This function is stub\n");
    return e_retval;
}



/* Authentication required */
#if 0
CtError curltweet_checkUserSubscriptionInList (CurlTweet p_s_ctHandle,
                                                              uint64_t u64_)

int main()
{
    ;
}
#endif
